﻿#region Using

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;

using DotNetMatrix;

using Emgu.CV;
using Emgu.CV.Structure;

#endregion

namespace EigenFaces {
	public static class Extenders {
		public static Point MiddlePoint(this Rectangle rect) {
			return new Point(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2);
		}

		public static Rectangle Scale(this Rectangle rect, double factor) {
			return new Rectangle {
				X = (int)(rect.X * factor),
				Y = (int)(rect.Y * factor),
				Width = (int)(rect.Width * factor),
				Height = (int)(rect.Height * factor)
			};
		}

		public static Rectangle Move(this Rectangle rect, Point pt) {
			rect.Offset(pt);
			return rect;
		}

		public static Rectangle Move(this Rectangle rect, int x, int y) {
			rect.Offset(x, y);
			return rect;
		}

		public static Matrix ToVector(this Image<Gray, double> image) {
			var result = new Matrix(image.Width * image.Height, 1);

			for( int column = 0; column < image.Width; column++ ) {
				for( int row = 0; row < image.Height; row++ ) {
					result.SetElement(column * image.Height + row, 0, image[row, column].Intensity);
				}
			}

			return result;
		}

		public static Matrix ToMatrix(this List<Matrix> vectors) {
			int width = vectors.Count;
			int height = vectors[0].RowDimension;
			var temp = new Matrix(height, width);
			for( int column = 0; column < width; column++ ) {
				for( int row = 0; row < height; row++ ) {
					temp[row, column] = vectors[column][row, 0];
				}
			}
			return temp;
		}

		public static Matrix GetAverage(this List<Matrix> list) {
			Matrix matrix = null;
			foreach( var mat in list ) {
				if( matrix == null ) {
					matrix = new Matrix(mat.RowDimension, mat.ColumnDimension);
				}
				matrix += mat;
			}
			matrix /= list.Count;
			return matrix;
		}

		public static Matrix Normalize(this Matrix matrix) {
			double min = double.MaxValue;
			double max = double.MinValue;
			for( int col = 0; col < matrix.ColumnDimension; col++ ) {
				for( int row = 0; row < matrix.RowDimension; row++ ) {
					if( matrix[row, col] < min ) {
						min = matrix[row, col];
					}
					if( matrix[row, col] > max ) {
						max = matrix[row, col];
					}
				}
			}

			var normalizedMatrix = new Matrix(matrix.RowDimension, matrix.ColumnDimension);
			double diff = max - min;
			for( int col = 0; col < matrix.ColumnDimension; col++ ) {
				for( int row = 0; row < matrix.RowDimension; row++ ) {
					normalizedMatrix[row, col] = (matrix[row, col] - min) / diff;
				}
			}
			return normalizedMatrix;
		}

		public static double EuclidDistance(this Matrix vectorA, Matrix vectorB) {
			if( vectorA.ColumnDimension != 1 || vectorB.ColumnDimension != 1 || vectorA.RowDimension != vectorB.RowDimension ) {
				throw new ArgumentException("Wrong dimensions of the vectors");
			}

			double result = 0;

			for( int i = 0; i < vectorA.RowDimension; i++ ) {
				result += Math.Pow((vectorA[i, 0] - vectorB[i, 0]), 2);
			}

			return Math.Sqrt(result);
		}

		public static double MinEuclidDistance(this Matrix baseVector, List<Matrix> vectors) {
			return getEuclidDistance(baseVector, vectors, true);
		}

		public static double MaxEuclidDistance(this Matrix baseVector, List<Matrix> vectors) {
			return getEuclidDistance(baseVector, vectors, false);
		}

		private static double getEuclidDistance(Matrix baseVector, List<Matrix> vectors, bool min) {
			double result = min ? double.MaxValue : double.MinValue;
			foreach( var vector in vectors ) {
				double dist = vector.EuclidDistance(baseVector);
				if( (result > dist && min) || (result < dist && !min) ) {
					result = dist;
				}
			}
			return result;
		}

		public static void RemoveClickEvent(this Button b) {
			FieldInfo f1 = typeof(Control).GetField("EventClick",
				BindingFlags.Static | BindingFlags.NonPublic);
			object obj = f1.GetValue(b);
			PropertyInfo pi = b.GetType().GetProperty("Events",
				BindingFlags.NonPublic | BindingFlags.Instance);
			EventHandlerList list = (EventHandlerList)pi.GetValue(b, null);
			list.RemoveHandler(obj, list[obj]);
		}
	}
}
